// Provide a function to get elements by class name an essential function missing in both major browsers



function getElementsByClassName(node, cls, tag) {
	var i, j, re, elm, elms, ismatch;
	var results = new Array();
	if (!document.getElementsByTagName) return results;
	// check type and validity of cls param
	if (typeof cls == 'string') cls = cls.split(' ');
	if (cls.length == 0) return results;
	// covert array of classnames to array of regexp objects matching classnames
	// doing this allows RegExp objects to be created just once
	for (i=0; i<cls.length; i++) {
		cls[i] = new RegExp('(^| )'+cls[i]+'( |$)');
	}
	// grab all the elements in node of type tag
	if (tag == null) tag = '*';
	elms = (tag == '*' && document.all) ? document.all : node.getElementsByTagName(tag); // IE5 does not like getElementsByTagName('*')
	// find matching elements
	for (i=0; elm=elms[i]; i++) {
		for (j=0; re=cls[j]; j++) {
			ismatch = true;
			if (!re.test(elm.className)) {
				ismatch = false;
				break;
			}
		}
		if (ismatch) results[results.length] = elm; // no Array.push in IE5
	}
	return results;
}

// Standardise the Text content functions accross browsers

function getText(control) 
{ 
    if (document.all) 
    { 
        return control.innerText; 
    } 
    else 
    { 
        return control.textContent; 
    } 
} 

function setText(control, value) 
{ 
    if (document.all) 
    { 
        control.innerText = value; 
    } 
    else 
    { 
        control.textContent = value; 
    } 
}

// A stack of functions to run onload/domready

startStack=function(){ 


};  

registerOnLoad = function(func) {
   var orgOnLoad = startStack;
   startStack = function () {
      orgOnLoad();
      func();
      return;
   }
}


// An additional stack of functions to run after the ajax

theStack=function(){ 


};  

registerOnAjax = function(func) {
   var orgOnAjax = theStack;
   theStack = function () {
     orgOnAjax();
      func();
      return;
   }
}

var ranOnload=false; // Flag to determine if we've ran the starting stack already.

if (document.addEventListener) {
  // Mozilla actually has a DOM READY event.
   document.addEventListener("DOMContentLoaded", function(){if (!ranOnload) {ranOnload=true; startStack();}}, false);
}  else if (document.all && !window.opera) {
  // This is the IE style which exploits a property of the (standards defined) defer attribute
  document.write("<scr" + "ipt id='DOMReady' defer=true " + "src=//:><\/scr" + "ipt>");  
  document.getElementById("DOMReady").onreadystatechange=function(){
    if (this.readyState=="complete"&&(!ranOnload)){
      ranOnload=true;
      startStack();
    }
  }
}

var orgOnLoad=window.onload;
window.onload=function() {
   if (typeof(orgOnLoad)=='function') {
      orgOnLoad();
   }
   if (!ranOnload) {
     ranOnload=true;
     startStack();
   }
}

//onload functions

function addLoadEvent(func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      if (oldonload) {
        oldonload();
      }
      func();
    }
  }
}

function findPos(obj){
	var curleft = curtop = 0;
	if (obj.offsetParent) {
		curleft = obj.offsetLeft
		curtop = obj.offsetTop
		while (obj = obj.offsetParent) {
			curleft += obj.offsetLeft
			curtop += obj.offsetTop
		}
	}
	return [curleft,curtop];
}


/*
	// The Javascript escape and unescape functions do not correspond
	// with what browsers actually do...
*/

function URLEncode(variable){

	var SAFECHARS = "0123456789" +					// Numeric
					"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +	// Alphabetic
					"abcdefghijklmnopqrstuvwxyz" +
					"-_.!~*'()";					// RFC2396 Mark characters
	var HEX = "0123456789ABCDEF";

	var plaintext = variable;
	var encoded = "";
	for (var i = 0; i < plaintext.length; i++ ) {
		var ch = plaintext.charAt(i);
	    if (ch == " ") {
		    encoded += "+";				// x-www-urlencoded, rather than %20
		} else if (SAFECHARS.indexOf(ch) != -1) {
		    encoded += ch;
		} else {
		    var charCode = ch.charCodeAt(0);
			if (charCode > 255) {
			    alert( "Unicode Character '" 
                        + ch 
                        + "' cannot be encoded using standard URL encoding.\n" +
				          "(URL encoding only supports 8-bit characters.)\n" +
						  "A space (+) will be substituted." );
				encoded += "+";
			} else {
				encoded += "%";
				encoded += HEX.charAt((charCode >> 4) & 0xF);
				encoded += HEX.charAt(charCode & 0xF);
			}
		}
	} // for

	return encoded;
}

/**
 * Lock: A Unified Locking Library
 * Thanks to the magic of the event stack in Firefox / IE, it is possible to
 * have your data be changed behind your back when using browser window
 * events. A basic lock will help stop that. An object is returned to
 * the requesting application which will say if a lock was obtained or not.
 *
 * This class is licensed under the New BSD License:
 * http://www.opensource.org/licenses/bsd-license.html
 *
 * Copyright (c) 2007 Jakob Heuser (jakob@felocity.org). All rights reserved.
 **/
if (!Lock) {
var Lock = function() {
    var locks = {};
    
    var normalize_namespace = function(name) {
        return ("c" + name).replace(/[^a-z0-9\-\_]/gi, "");
    };

    return {
        declare: function() {
            for (var i = 0; i < arguments.length; i++) {
                if (!locks[normalize_namespace(arguments[i])]) {
                    locks[normalize_namespace(arguments[i])] = new Array();
                }
            }
        },
        obtain: function(space) {
            // atomic assignment, no 2 objects are same
            var lock = new Object();
            
            // no namespace? problem
            space = normalize_namespace(space);
            if (!locks[space]) {
                throw "Namespaces must be declared before getting into locks.";
            }
    
            // atomic op for as long as JS is single threaded
            // whenever JS multi-threads, this one call is synchronized
            locks[space].push(lock);
    
            // safely clean lock_owner
            if (locks[space][0] === lock) {
                locks[space] = [locks[space][0]];
            }
    
            var lock_obj = {
                isOwner: function() {
                    return (locks[space][0] === lock);
                },
                release: function() {
                    if (locks[space][0] === lock) {
                        locks[space] = new Array();
                    }
                }
            };
    
            return lock_obj;
        }
    };
}();
}

/**
 * Just In Time (JIT) Loader
 * JIT makes it easy to load one or more JavaScript files on demand. It's goal
 * is to encourage developers to only load scripts when they need to as
 * opposed to overloading the HEAD of their document.
 *
 * Many of the base loading concepts are attrributed to LazyLoad, developed
 * by Ryan Grove, please see copyright information below for additional
 * details. This software is licensed under the New BSD License:
 * http://www.opensource.org/licenses/bsd-license.html
 *
 * LazyLoad segments Copyright (c) 2007 Ryan Grove (ryan@wonko.com). All rights reserved.
 * JIT segments Copyright (c) 2007 Jakob Heuser (jakob@felocity.org). All rights reserved.
 *
 * For additional details, please check out the following usage guides:
 * LazyLoad: http://wonko.com/article/527
 * JITLoad:  http://www.felocity.org/blog/article/just_in_time_loader_for_javascript/
 *
 * Version: 1.0.0; 1.0.3 (LazyLoad)
 **/
if (!JIT) {
var JIT = function() {
    
    /**
     * Denotes an object that is pending a requestComplete() call
     * it is null if there is no request in progress
     **/
    var pending = {};
    
    /** 
     * A mutable array of the current script IDs in use. This makes
     * cleanup of the scripts after they have finished loading easier.
     **/
    var script_ids = {};
    
    /**
     * A counter for the total number of scripts we have created.
     * Helps to ensure clean loading without collisions.
     **/
    var script_id_counter = 0;
    
    /**
     * A variable that defines the lock owner.
     * Using the counter and owner, a function can determine if they
     * are the lock.
     **/
    var lock_owner = null;
    
    /**
     * The prefix our our custom IDs. This ensures we don't collide w/ stuff
     **/
    var script_id_prefix = "jit-gen";
    
    /**
     * An index of URLs that have been loaded
     * At the expense of more memory, this speeds up scanning for all
     * included scripts
     **/
    var loaded_scripts = {};

    /**
     * sets an IE version based on @_jscript_version
     * replace if cc ever gets IE versioning, this is used for CSS
     **/
    var IEVersion = /*@cc_on function(){ switch(@_jscript_version){ case 1.0:return 3; case 3.0:return 4; case 5.0:return 5; case 5.1:return 5; case 5.5:return 5.5; case 5.6:return 6; case 5.7:return 7; case 5.8:return 8; }}()||@*/0;
    
    /**
     * Container for holding the document head, so we only do it once
     **/
    var document_head = null;
    
    /**
     * Declare our locks
     **/
    var LOCK_WRITING_TO_DOM = "JIT_dom_write";
    var LOCK_DOM_CLEANUP = "JIT_dom_clean";
    var LOCK_GET_SEQUENCE_ID = "JIT_sequence_id";
    Lock.declare(LOCK_GET_SEQUENCE_ID, LOCK_WRITING_TO_DOM, LOCK_DOM_CLEANUP);
    
    /**
     * creates a unique ID using the counter and prefix, runs inside of a dom write space
     * @return {string}
     **/
    var generateId = function() {
        script_id_counter++;
        return script_id_prefix + script_id_counter;
    };
    
    /**
     * Detect all loaded scripts, and add their URLs to the list
     * If there is a faster method than getting the elements by tag name
     * it should be used instead.
     * This runs on loadOnce so that we can see if other scripts also
     * added their own JS.
     * @return {null}
     **/
    var detectLoadedScripts = function() {
        var script_nodes = document.getElementsByTagName("script");
        var css_nodes = document.getElementsByTagName("link");

        for (var i = 0; i < script_nodes.length; i++) {
            // skip sourcelss scripts
            var node = script_nodes[i];
            if (!node.src || node.src.length == 0) {
                continue;
            }
            loaded_scripts[normalizeScriptPath(node.src)] = true;
        }
        for (var j = 0; j < css_nodes.length; j++) {
            // skip sourcelss css or wrong types
            var node = css_nodes[j];

            if (!node.href || node.href.length == 0 ||
                !node.rel || node.rel.toString().toLowerCase() != "stylesheet" ||
                !node.type || node.type.toString().toLowerCase() != "text/css") {
            
                continue;
            }

            loaded_scripts[normalizeScriptPath(node.href)] = true;
        }
    };
    
    /**
     * A helper function which normalizes the script path
     * the resulting path can be used as a property name in an object
     * @param {string} path to normalize
     * @return {string} normalized path
     **/
    var normalizeScriptPath = function(path) {
        return "s" + escape(path);
    };
    
    /**
     * Handle a load function for Javascript or CSS
     * @see load
     * @see loadOnce
     * @param {type} a type to load, either script or css
     * @param {boolean} once if true, load will ensure everything loads only once
     **/
    var handleLoad = function(urls, verifier, callback, obj, scope, type, once) {
        // we wait on document.body, otherwise we can't be certain we have
        // a closed HEAD tag in IE6 for insertion
        if (!document.body) {
            window.setTimeout(function() { handleLoad(urls, verifier, callback, obj, scope, type, once); }, 50);
            return;
        }
        
        // ---------------------------
        // ---- BEGIN CRTICIAL SECTION
        // ---------------------------
        
        // if you are not the lock owner, then your request goes into wait mode
        // techincally a spinlock.  Wait is fixed at 10ms right now, can change
        // to reflect number of current "threads" later
        var lock = Lock.obtain(LOCK_GET_SEQUENCE_ID);
        if (!lock.isOwner()) {
            window.setTimeout(function() { handleLoad(urls, verifier, callback, obj, scope, type, once); }, 10);
            return;
        }
        
        // obtained lock
        
        // assign the document head if we haven't yet
        if (!document_head) {
            document_head = document.getElementsByTagName('head')[0];
        }
        
        // get an ID for our sequence
        var sequence_id = generateId();
        pending[sequence_id] = {};
        script_ids[sequence_id] = [];
        
        // scrape all loaded scripts in case things have changed
        detectLoadedScripts();
        
        // done, we completed critical code section
        lock.release();
        
        // -------------------------
        // ---- END CRTICIAL SECTION
        // -------------------------
        
        // cast URLs to an array if we need to
        urls = (urls.constructor === Array) ? urls : [urls];
        
        // if verifier was skipped or nulled, then we need to make one
        if (!verifier || typeof(verifier) != "function") {
            verifier = function() { return true; };
        }
        
        // hold onto the pending object for requestComplete and loadComplete
        pending[sequence_id] = {urls: urls, verifier: verifier, callback: callback, obj: obj, scope: scope, type: type, once: once, lock: lock};
        
        // if we are running in loadOnce mode
        if (once) {
            var urls_to_load = [];
            for (var i = 0; i < urls.length; i += 1) {
                var loaded = (loaded_scripts[normalizeScriptPath(urls[i])]) ? true : false;
                if (!loaded) {
                    urls_to_load.push(urls[i]);
                }
            }
            
            // do we have any URLs to load? If not, loading is complete and we are done
            if (urls_to_load.length <= 0) {
                loadComplete(sequence_id);
                return;
            }
            
            // there is stuff to load still
            // redefine URls by our new definition, and our pending
            urls = urls_to_load;
            pending[sequence_id] = {urls: urls, verifier: verifier, callback: callback, obj: obj, scope: scope, type: type, once: once, lock: lock};
        }
        
        if (type == "js") {
            insertScripts(urls, sequence_id);
        }
        else if (type == "css" || (type.match(/^css/i) && type == "css"+IEVersion)) {
            insertStyles(urls, sequence_id);
        }
        else {
            // whatever we had, we can't use... release the lock
            lock.release();
            JIT.scriptsComplete(sequence_id);
        }

    };
    
    var insertStyles = function(urls, sequence_id) {
        
        // Cast urls to an Array.
        urls = urls.constructor === Array ? urls : [urls];
        
        var node;
        
        for (var i = 0; i < urls.length; i += 1) {
    
            // create a unique ID and add to our ID list
            var sc_id = generateId();
            //script_ids.push(sc_id);
    
            // create the script object, and append it to the head
            node = document.createElement('link');
            node.id = sc_id;
            node.href = urls[i];
            node.rel = "stylesheet";
            node.type = "text/css";
            node.media = "screen";
    
            writeNode(node);
        }
        
        // in MSIE, we will need to listen to the onreadystatechange
        // if the file is cached, we may not even see "loaded" as an option
        // and may instead see "complete". Because of this, we need to scan
        // for both. Script loading is linear, so we only need to watch
        // the last script we were inserting
        if (IEVersion) {
            node.onreadystatechange = function () {
                if (this.readyState == 'loaded' || this.readyState == 'complete') {
                    JIT.scriptsComplete(sequence_id);
                }
            };
        }
        else {
            // this is a non MSIE browser. We can use a safer method of
            // detecting when a script is done. We insert a small scriptlet
            // at the end of all our script objects which executes the
            // requestComplete() code.
            var sc_id = generateId();
            script_ids[sequence_id].push(sc_id);

            var smart_script = document.createElement('script');        
            smart_script.id = sc_id;
            smart_script.type = "text/javascript";
            smart_script.appendChild(document.createTextNode("JIT.scriptsComplete('"+sequence_id+"');"));

            writeNode(smart_script);
        }
        
        // release DOM writing lock
        if (pending[sequence_id]) {
            pending[sequence_id].lock.release();
        }
    };
    
    var insertScripts = function(urls, sequence_id) {
        
        // Cast urls to an Array.
        urls = urls.constructor === Array ? urls : [urls];
        
        // Load the scripts at the specified URLs.
        var script;

        for (var i = 0; i < urls.length; i += 1) {
    
            // create a unique ID and add to our ID list
            var sc_id = generateId();
            script_ids[sequence_id].push(sc_id);
    
            // create the script object, and append it to the head
            script = document.createElement('script');
            script.id = sc_id;
            script.src = urls[i];
            script.type = "text/javascript";
    
            writeNode(script);
        }

        // no script at this point, we're in trouble
        if (!script) {
            // release DOM writing lock
            if (pending[sequence_id]) {
                pending[sequence_id].lock.release();
            }
            return;
        }

        // in MSIE, we will need to listen to the onreadystatechange
        // if the file is cached, we may not even see "loaded" as an option
        // and may instead see "complete". Because of this, we need to scan
        // for both. Script loading is linear, so we only need to watch
        // the last script we were inserting
        if (IEVersion) {
            script.onreadystatechange = function () {
                if (this.readyState == 'loaded' || this.readyState == 'complete') {
                    JIT.scriptsComplete(sequence_id);
                }
            };
        }
        else {
            // this is a non MSIE browser. We can use a safer method of
            // detecting when a script is done. We insert a small scriptlet
            // at the end of all our script objects which executes the
            // requestComplete() code.
            var sc_id = generateId();
            script_ids[sequence_id].push(sc_id);

            var smart_script = document.createElement('script');        
            smart_script.id = sc_id;
            smart_script.type = "text/javascript";
            smart_script.appendChild(document.createTextNode("JIT.scriptsComplete('"+sequence_id+"');"));

            writeNode(smart_script);
        }
        
        // release DOM writing lock
        if (pending[sequence_id]) {
            pending[sequence_id].lock.release();
        }
    };
    
    var writeNode = function(node) {
        var timer = null;
        var retry_in = 100;
        // a function that tries to get a lock for DOM write
        // once it does, it inserts
        var processWrite = function() {
            // ---------------------------
            // ---- BEGIN CRTICIAL SECTION
            // ---------------------------
            var lock = Lock.obtain(LOCK_WRITING_TO_DOM);
            if (!lock.isOwner()) {
                timer = window.setTimeout(processWrite, retry_in);
                return;
            }
            window.clearTimeout(timer);
            timer = null;
            document_head.appendChild(node);
            lock.release();
        };
        
        // calls processWrite on a setTimeout that lets other events run
        timer = window.setTimeout(processWrite, retry_in);
    };
    
    /**
     * A helper function which completes the request
     * it fires off any callbacks that are required, and hands off the lock
     * to the next request in line
     **/
    var loadComplete = function(sequence_id, lock) {
        // there is a theoretical window where we could resolve a loadComplete
        // with a loadComplete waiting... if that happens, just return
        if (!pending[sequence_id]) {
            return;
        }
        
        // try and lock on DOM cleanup
        if (!lock) {
            var lock = Lock.obtain(LOCK_DOM_CLEANUP);
        }
        
        // if not owner, try again with getting a new lock
        if (!lock.isOwner()) {
            window.setTimeout(function() { loadComplete(sequence_id); }, 10);
            return;
        }

        // run the current verifier until it passes
        if (pending[sequence_id].verifier && !pending[sequence_id].verifier.call(window)) {
            // did not pass, try again in X seconds using same (valid) lock
            window.setTimeout(function() { loadComplete(sequence_id, lock); }, 100);
            return;
        }
        
        // release DOM writing lock if not done
        if (pending[sequence_id]) {
            pending[sequence_id].lock.release();
        }
        
        // redetect our loaded scripts at this point
        detectLoadedScripts();

        // remove any script IDs we have made, they are all done... I mean, if
        // there are any
        if (script_ids[sequence_id]) {
            while (script_ids[sequence_id].length > 0) {
               var sc_id = script_ids[sequence_id].shift();
              

if (document.getElementById(sc_id)) {

var script = document.getElementById(sc_id);
script.parentNode.removeChild(script);

}
            }
            script_ids[sequence_id] = null;
        }
        
        // Execute the callback.
        if (pending[sequence_id] && pending[sequence_id].callback) {
            if (pending[sequence_id].obj) {
                if (pending[sequence_id].scope) {
                    pending[sequence_id].callback.call(pending[sequence_id].obj);
                }
                else {
                    pending[sequence_id].callback.call(window, pending[sequence_id].obj);
                }
            }
            else {
                pending[sequence_id].callback.call();
            }
        }

        // clear our pending object for the next request
        // not required, just nice to clean
        pending[sequence_id] = null;
        
        // release the lock
        lock.release();
    };
    
    /**
     * Returns a batch object for chain processing.
     * the returned object is the easiest to work with in the JIT loader
     * and its functionality is documented similarly to JIT. However,
     * for load, loadOnce, and addCSS, there is no callbacks involved. Instead,
     * the system uses its run function as a callback method to unfurl the
     * stack created
     * @return JIT Batch Object
     * @see JIT.load
     * @see JIT.loadOnce
     * @see JIT.addCSS
     **/
    var JIT_Chain = function() {
        var stack = [];
        var run_callback = null;
        var run_object = null;
        var run_scope = null;
    
        // return object
        return {
            load: function(urls, verifier) {
                stack.push({type: "js", once: false, urls: urls, verifier: verifier});
                return this;
            },
            loadOnce: function(urls, verifier) {
                stack.push({type: "js", once: true, urls: urls, verifier: verifier});
                return this;
            },
            addCSS: function(urls, verifier, ie_version) {
                stack.push({type: "css", once: true, urls: urls, verifier: verifier, ie: ie_version});
                return this;
            },
            
            /**
             * Executes the stack of objects, using a basic form of recursion
             * @param function the callback function to run
             * @param obj the object to include in the callback
             * @param scope if true, the callback will be ran in the object's scope
             **/
            onComplete: function(callback, obj, scope) {
                var that = this;
            
                // store the run callback the first time we enter the onComplete
                if (!run_callback) {
                    
                    if (!callback) {
                        callback = function() {};
                    }
                    run_callback = callback;
                    run_object = obj;
                    run_scpe = scope;
                }
            
                // no stack, we are done, run the callback
                if (stack.length == 0) {
                    if (obj) {
                        if (scope) {
                            run_callback.call(obj);
                        }
                        else {
                            run_callback.call(window, obj);
                        }
                    }
                    else {
                        run_callback.call();
                    }
                
                    return;
                }
        
                // start unstacking
                var next_call = stack.shift();
            
                // call a run op for this
                if (next_call.type == "js") {
                    if (next_call.once) {
                        JIT.loadOnce(next_call.urls, next_call.verifier, that.onComplete, that, true);
                    }
                    else {
                        JIT.load(next_call.urls, next_call.verifier, that.onComplete, that, true);
                    }
                }
                else if (next_call.type == "css") {
                    if (next_call.ie) {
                        JIT.addCSS(next_call.urls, next_call.verifier, that.onComplete, that, true, next_call.ie);
                    }
                    else {
                        JIT.addCSS(next_call.urls, next_call.verifier, that.onComplete, that, true);
                    }
                }
            }
        };
    };
    
    // begin public interface
    return {
        /**
         * Loads the specified script(s) and then sets up a call to requestComplete
         * this is the meat of the JIT loader.
         * @param {string|array} the URLs to load
         * @param {function} verifier a funtion definition that asserts load is done
         * @param {function} callback a function definition to call when loaded
         * @param {object} obj an object to pass to the callback function [object]
         * @param {boolean} scope if true, *callback* will be scoped to *obj*
         **/
        load: function(urls, verifier, callback, obj, scope) {
            handleLoad(urls, verifier, callback, obj, scope, "js", false);
        },
        
        addCSS: function(urls, verifier, callback, obj, scope, ie_restrict) {
            if (!ie_restrict) {
                ie_restrict = "";
            }
            handleLoad(urls, verifier, callback, obj, scope, "css"+ie_restrict, true);
        },
        
        /**
         * Load a script only once. If that script has existed in our document.
         * When initialized, we poll and take a capture of all scripts. If
         * any urls are found, they will be discarded.
         * @param {string|array} the URLs to load
         * @param {function} verifier a funtion definition that asserts load is done
         * @param {function} callback a function definition to call when loaded
         * @param {object} obj an object to pass to the callback function [object]
         * @param {boolean} scope if true, *callback* will be scoped to *obj*
         **/
        loadOnce: function(urls, verifier, callback, obj, scope) {
            handleLoad(urls, verifier, callback, obj, scope, "js", true);
        },
        
        startChain: function() {
            return JIT_Chain();
        },
        
        /**
         * Runs the current verifier until it passes, then calls loadComplete
         **/
        scriptsComplete: function(sequence_id) {
            // loadComplete call to hand off and clean up
            loadComplete(sequence_id);
        }
    };
}();
}

/*
Ajaxcaller
http://ajaxify.com/run/testAjaxCaller/
To stndardise AJAX between browsers and use PUT and delete for REST
*/

var ajaxCaller = {

  shouldDebug: false,
  shouldEscapeVars: false,
  shouldMakeHeaderMap: true,

  calls : new Array(),
  pendingResponseCount : 0,

   /**************************************************************************
      PUBLIC METHODS
   *************************************************************************/

  getXML: function(url, callbackFunction) {
    this.get(url, null, callbackFunction, true, null);
  },

  getPlainText: function(url, callbackFunction) {
    this.get(url, null, callbackFunction, false, null);
  },

  postForPlainText: function(url, vars, callbackFunction) {
    this.postVars(url, vars, null, callbackFunction, false,
                    null, "POST", null, null, null);
  },

  postForXML: function(url, vars, callbackFunction) {
    this.postVars(url, vars, null, callbackFunction, true,
                    null, "POST", null, null, null);
  },

  get: function(url, urlVars, callbackFunction, expectingXML, callingContext) {
    this._callServer(url, urlVars, callbackFunction, expectingXML,
                    callingContext, "GET", null, null, null);
  },

  postVars:
    function(url, bodyVars, optionalURLVars, callbackFunction, expectingXML,
             callingContext) {
      this._callServer(url, optionalURLVars, callbackFunction, expectingXML,
                      callingContext, "POST", bodyVars, null, null);
  },

  postBody:
    function(url, optionalURLVars, callbackFunction, expectingXML,
             callingContext, bodyType, body) {
      this._callServer(url, optionalURLVars, callbackFunction, expectingXML,
                      callingContext, "POST", null, bodyType, body);
  },

  putBody:
    function(url, optionalURLVars, callbackFunction, expectingXML,
             callingContext, bodyType, body) {
      this._callServer(url, optionalURLVars, callbackFunction, expectingXML,
                      callingContext, "PUT", null, bodyType, body);
  },

  options:
    function(url, optionalURLVars, callbackFunction, expectingXML,
             callingContext, bodyType, body) {
      this._callServer(url, optionalURLVars, callbackFunction, expectingXML,
                      callingContext, "OPTIONS", null, bodyType, body);
  },

  trace:
    function(url, optionalURLVars, callbackFunction, expectingXML,
             callingContext, bodyType, body) {
      this._debug("trace");
      this._callServer(url, optionalURLVars, callbackFunction, expectingXML,
                      callingContext, "TRACE", null, bodyType, body);
  },

  deleteIt: function(url, urlVars, callbackFunction,
                     expectingXML, callingContext) {
    this._callServer(url, urlVars, callbackFunction, expectingXML,
                    callingContext, "DELETE", null, null, null);
  },

  head: function(url, urlVars, callbackFunction, expectingXML, callingContext)
  {
    this._callServer(url, urlVars, callbackFunction, expectingXML,
                    callingContext, "HEAD", null, null, null);
  },

  /**************************************************************************
     PRIVATE METHODS
  *************************************************************************/

  _callServer: function(url, urlVars, callbackFunction, expectingXML,
                       callingContext, requestMethod, bodyVars,
                       explicitBodyType, explicitBody) {

    if (urlVars==null) {
      urlVars = new Array();
    }

    this._debug("_callServer() called. About to request URL\n"
                + "call key: [" + this.calls.length + "]\n"
                + "url: [" + url + "]\n"
                + "callback function: [" + callbackFunction + "]\n"
                + "treat response as xml?: [" + expectingXML + "]\n"
                + "Request method?: [" + requestMethod + "]\n"
                + "calling context: [" + callingContext + "]\n"
                + "explicit body type: [" + explicitBodyType + "]\n"
                + "explicit body: [" + explicitBody + "]\n"
                + "urlVars: [" + util.describe(urlVars) + "]\n"
                + "bodyVars: [" + util.describe(bodyVars) + "]"
              );


    var xReq = this._createXMLHttpRequest();
    xReq.onreadystatechange = function() {
      ajaxCaller._onResponseStateChange(call);
    }

    var call = {xReq: xReq,
                callbackFunction: callbackFunction,
                expectingXML: expectingXML,
                callingContext: callingContext,
                url: url};

    if (urlVars!=null) {
      var urlVarsString = this._createHTTPVarSpec(urlVars);
      if (urlVarsString.length > 0) { // TODO check if appending with & instead
        url += "?" + urlVarsString;
      }
    }

    xReq.open(requestMethod, url, true);

    if (   requestMethod=="GET"
        || requestMethod=="HEAD"
        || requestMethod=="DELETE") {
      this._debug("Body-less request to URL " + url);
      xReq.send(null);
      return;
    }

    if (   requestMethod=="POST"
        || requestMethod=="PUT"
        || requestMethod=="OPTIONS"
        || requestMethod=="TRACE") {
      bodyType = null;
      body = null;
      if (explicitBodyType==null) { // It's a form
        bodyType = 'application/x-www-form-urlencoded; charset=UTF-8';
        body = this._createHTTPVarSpec(bodyVars);
      } else {
        bodyType = explicitBodyType;
        body = explicitBody;
      }
      this._debug("Content-Type: [" + bodyType + "]\nBody: [" + body + "].");
      xReq.setRequestHeader('Content-Type',  bodyType);
      xReq.send(body);
      return;
    }

    this._debug("ERROR: Unknown Request Method: " + requestMethod);


  },

  // The callback of xmlHttpRequest is a dynamically-generated function which
  // immediately calls this function.
  _onResponseStateChange: function(call) {

    xReq = call.xReq;

    if (xReq.readyState < 4) { //Still waiting
      return;
    }

    if (xReq.readyState == 4) { //Transmit to actual callback
      this._debug("Call " + util.describe(call)
                + " with context [" + call.callingContext+"]"
                + " to " + call.url + " has returned.");
      callbackFunction = call.callbackFunction;
      if (!callbackFunction) { // Maybe still loading, e.g. in another JS file
        setTimeout(function() {
          _onResponseStateChange(call);
        }, 100);
      }
      var content = call.expectingXML ? xReq.responseXML : xReq.responseText;
      responseHeaders = xReq.getAllResponseHeaders();
      headersForCaller = this.shouldMakeHeaderMap ?
        this._createHeaderMap(responseHeaders) : responseHeaders;
      callbackFunction(content, headersForCaller, call.callingContext);
    }

    call = null; // Technically the responsibility of GC
    this.pendingResponseCount--;

  },

  // Browser-agnostic factory function
  _createXMLHttpRequest: function() {
    if (window.XMLHttpRequest) {
      return new XMLHttpRequest();
    } else if (window.ActiveXObject) {
      return new ActiveXObject('Microsoft.XMLHTTP')
    } else {
      _error("Could not create XMLHttpRequest on this browser");
      return null;
    }
  },

  _createHTTPVarSpec: function(vars) {
      var varsString = "";
      for( key in vars ) {
        var value = vars[key];
        if (this.shouldEscapeVars) {
          escapePlusRE =  new RegExp("\\\+");
          value = value.replace(escapePlusRE, "%2B");
        }
        varsString += '&' + key + '=' + value;
      }
      if (varsString.length > 0) {
        varsString = varsString.substring(1); // chomp initial '&'
      }
      this._debug("Built var String: " + varsString)
      return varsString;
   },

  /* Creates associative array from header type to header */
  _createHeaderMap: function(headersText) {
    extractedHeaders = headersText.split("\n");
    delete extractedHeaders[extractedHeaders.length]; // Del blank line at end
    headerMap = new Array();
    for (i=0; i<extractedHeaders.length-2; i++) {
      head = extractedHeaders[i];
      fieldNameEnding = head.indexOf(":");
      field = head.substring(0, fieldNameEnding);
      value = head.substring(fieldNameEnding + 2, head.length);
      value = value.replace(/\s$/, "");
      headerMap[field] = value;
    }
    return headerMap;
  },

  _debug: function(message) {
      if (this.shouldDebug) {
        alert("AjaxJS Message:\n\n" + message);
      }
  },

  _error: function(message) {
      if (this.shouldDebug) {
        alert("AjaxJS ERROR:\n\n" + message);
      }
  }

};


function loadjscssfile(filename, filetype){
 if (filetype=="js"){ //if filename is a external JavaScript file
  var fileref=document.createElement('script')
  fileref.setAttribute("type","text/javascript")
  fileref.setAttribute("src", filename)
 }
 else if (filetype=="css"){ //if filename is an external CSS file
  var fileref=document.createElement("link")
  fileref.setAttribute("rel", "stylesheet")
  fileref.setAttribute("type", "text/css")
  fileref.setAttribute("href", filename)
fileref.setAttribute("media", "print")
 }
 if (typeof fileref!="undefined")
  document.getElementsByTagName("head")[0].appendChild(fileref)
}

/*
	parseUri 1.2.1
	(c) 2007 Steven Levithan <stevenlevithan.com>
	MIT License
*/

function parseUri (str) {
	var	o   = parseUri.options,
		m   = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
		uri = {},
		i   = 14;
function isDefined(variable){
return (!(!(document.getElementById(variable))))
}
	while (i--) uri[o.key[i]] = m[i] || "";

	uri[o.q.name] = {};
	uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
		if ($1) uri[o.q.name][$1] = $2;
	});

	return uri;
};

parseUri.options = {
	strictMode: false,
	key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
	q:   {
		name:   "queryKey",
		parser: /(?:^|&)([^&=]*)=?([^&]*)/g
	},
	parser: {
		strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
		loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
	}
};
